#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "log.h"
#include "csvparser.h"
#include "types.h"

#define DEV_INFO_ITEM_SIZE  128


typedef  struct _tag_csv_parser_
{
    struct list_head head;
    struct list_head* current;
    int length;
    char filename[64];
    FILE * file;
    pthread_mutex_t mutex;
} Parser;


static void _free_list_entry(Parser* parser)
{
    int count = 0;
    if ( parser )
    {
        CsvNode* dev = NULL;
        CsvNode* temp = NULL;
        list_for_each_entry(dev, &parser->head, list)
        {   
            ++count;
            
            free(temp);
            free(dev->name);
            free(dev->code);
            temp = dev;
        }
        printf("csvparser: free list entry count: %d\n", count);
        free(temp);

        INIT_LIST_HEAD(&parser->head);
    }

}

static int _parse_csv_line(FILE * file, char* code, char* name)
{
    int ret = 1;

    if ( file )
    {
        fscanf(file, "%[^,\n]", code);
        if ( strlen(code) <= 0 )
        {
            printf("csvparser: read code error\n");
            ret = 0;
        }
        
        fgetc(file);

        fscanf(file, "%[^,\n]", name);
        if ( strlen(name) <= 0 )
        {
            printf("csvparser: read name error\n");
            ret = 0;
        }
 
        //fgetc(file);
    }

    return ret;
}

static CsvNode * _create_node(const char* code, const char* name)
{
    CsvNode *node =  (CsvNode*)(malloc(sizeof(CsvNode)));

    if ( node && code && name )
    {
        node->name = strdup(name);
        node->code = strdup(code);
        //node->avg = _combine_avg_str(code, "Avg");

        if ( !node->code || !node->name )
        {
            free(node->name);
            free(node->code);
            free(node);
            node = NULL;
        }
    }
    
    return node;
}


static FILE* _load_file(const char *filename)
{
    FILE * csv = NULL;

    if ( (csv = fopen(filename, "r")) == NULL ) 
        LOG_E("csvparser: cannot open %s\n", filename);
    
    return csv;
}

static int _find_code(Parser* parser, const char* code)
{
    int ret = 0;
    CsvNode* dev = NULL;

    list_for_each_entry(dev, &parser->head, list)
    {
        if ( strcmp(dev->code, code) == 0 )
        {
            ret = 1;
            break;
        }
    }

    return ret;
}

void csvparser_read_csv_to_code_list(CsvParser* parser)
{
    Parser* csv = (Parser*)parser;
    csv->file = _load_file(csv->filename);

    if ( csv && csv->file )
    {
        char name[DEV_INFO_ITEM_SIZE] = { 0 };
        char code[DEV_INFO_ITEM_SIZE] = { 0 };

        fgets(code, DEV_INFO_ITEM_SIZE, csv->file);

        while ( 1 )
        {
            if ( _parse_csv_line(csv->file, code, name) )
            {
                if ( !_find_code(csv, code) )
                {
                    CsvNode* m_node = _create_node(code, name);
                    if ( m_node )
                    {
                        list_add_tail(&m_node->list, &csv->head);
                        ++csv->length;
                        
                    }
                    else 
                    {
                        LOG_E("read csv file end\n");
                        LOG_E("is not enough memory space to create csv node\n");
                        break;
                    }
                }
            }
           
            if ( fgetc(csv->file) == EOF )
            {
                LOG_I("read csv file end\n");
                fclose(csv->file);
                //printf("total: %d\n", csv->length);
                break;
            }
                
                
        }

    }
}


CsvParser csvparser_creat(const char *filename)
{
    Parser *parser = (Parser*)(malloc(sizeof(Parser)));
    
    if ( parser && filename )
    {
        parser->length = 0;
        parser->file = NULL;
        INIT_LIST_HEAD((struct list_head*)&parser->head);
        strcpy(parser->filename, filename);
        if ( pthread_mutex_init(&parser->mutex, NULL) != 0 )
        {
            printf("csvparser: mutex init failed\n");
            free(parser);
            parser = NULL;
        }
    }

    return ((CsvParser)parser);
}


void csvparser_free(CsvParser parser)
{
    if ( parser )
    {
        Parser* csv = (Parser*)parser;

        _free_list_entry(csv);
        pthread_mutex_destroy(&csv->mutex);
        if ( csv->file )
            fclose(csv->file);
        free(csv);
    }
}

int csvparser_get_length(CsvParser parser)
{
    int ret = 0;

    if ( parser )
    {
        Parser* csv = (Parser*)parser;
        ret = csv->length;
    }

    return ret;
}


CsvNode* csvparser_get_node(CsvParser parser, const char* code)
{
    CsvNode *ret = NULL;
    int find = 0;
    Parser* csv = (Parser*)parser;

    if ( parser && code )
    {
        list_for_each_entry(ret, &csv->head, list)
        {
            if ( !strcmp(ret->code, code) )
            {
                find = 1;
                break;
            }
        }
    }

    return (find ? ret : NULL);
}

void csvparser_begin(CsvParser parser)
{
    Parser* csv = (Parser*)parser;

    if ( csv )
    {
        csv->current = csv->head.next;
    }
}


void csvparser_next(CsvParser parser)
{
    Parser* csv = (Parser*)parser;

    (csv ? csv->current = csv->current->next : csv->current);
    
}


int csvparser_is_end(CsvParser parser)
{
    int ret = 0;
    Parser* csv = (Parser*)parser;

    if ( csv )
    {
        ret = (csv->current == &csv->head);
    }

    return ret;
}

CsvNode* csvparser_current(CsvParser parser)
{
    CsvNode *ret = NULL;
    Parser* csv = (Parser*)parser;

    if ( csv && csv->current )
    {
        ret = list_entry(csv->current, CsvNode, list);
    }

    return ret;
}

void csvparser_mutex_lock(CsvParser* parser)
{
    Parser* csv = (Parser*)parser;

    if ( csv )
    {
        pthread_mutex_lock(&csv->mutex);
    }
}

void csvparser_mutex_unlock(CsvParser* parser)
{
     Parser* csv = (Parser*)parser;

    if ( csv )
    {
        pthread_mutex_unlock(&csv->mutex);
    }
}

void csvparser_free_list(CsvParser* parser)
{
    Parser* csv = (Parser*)parser;
    if ( csv )
    {
        _free_list_entry(csv);
    }
    
}